/*
	Copyright 2011 Dario Seitel, Sebastian Mayer. All rights reserved.

	Redistribution and use in source and binary forms, with or without modification, are
	permitted provided that the following conditions are met:

	   1. Redistributions of source code must retain the above copyright notice, this list of
		  conditions and the following disclaimer.

	   2. Redistributions in binary form must reproduce the above copyright notice, this list
		  of conditions and the following disclaimer in the documentation and/or other materials
		  provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
	WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
	FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
	ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
	ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	The views and conclusions contained in the software and documentation are those of the
	authors and should not be interpreted as representing official policies, either expressed
	or implied, of <copyright holder>.
*/

#include "AribaNode.h"

#include <stdio.h>
#include <stdlib.h>
#include "GameEngine.h"
#include "ariba/utility/configuration/Configuration.h"
#include "messages/GameMessage.h"
#include "messages/GameConnect.h"
#include "messages/RegionConnect.h"
#include "messages/ConnectAccept.h"
#include "messages/ErrorMessage.h"
#include "messages/StringContainerMessage.h"
#include "messages/PlayerStatus.h"
#include "messages/MoveUpDown.h"
#include "messages/MoveRotation.h"
#include "messages/ShotMessage.h"
#include "messages/RegionMapMessage.h"
#include "messages/RegionAllocationMessage.h"
#include "messages/evaluators/MessageEvaluator.h"
#include "messages/evaluators/InitiatorMessageEvaluator.h"
#include "timer/MessageUpDownTimer.h"
#include "timer/MessageRotationTimer.h"
#include "timer/GroupPingTimer.h"
#include "timer/HostTimeoutTimer.h"
#include "../tools/DataTypes.h"
#include "boost/date_time/posix_time/posix_time.hpp"

ServiceID AribaNode::ARIBANODE_SERVICEID = ServiceID(111);
bool AribaNode::running = true;
bool AribaNode::isInitiator = false;
bool AribaNode::isHost = false;

int AribaNode::TimerMoveIntervall = 2000;
int AribaNode::GroupPingTimerIntervall = 500;
int AribaNode::HostTimeoutTimerIntervall = 1000;

AribaNode::AribaNode() : messageEvaluator(NULL), ready(false) {
	hostName = "";
	hostID = ariba::NodeID::UNSPECIFIED;

	upDownTimer = new MessageUpDownTimer(this);
	upDownTimer->setInterval(TimerMoveIntervall, false);

	rotationTimer = new MessageRotationTimer(this);
	rotationTimer->setInterval(TimerMoveIntervall, false);

	groupPingTimer = new GroupPingTimer(this);
	groupPingTimer->setInterval(GroupPingTimerIntervall, false);

	hostTimeoutTimer = new HostTimeoutTimer(this);
	hostTimeoutTimer->setInterval(HostTimeoutTimerIntervall, false);
}

void AribaNode::startup() {
	logging_rootlevel_error();

	// create ariba module
	ariba = new AribaModule();
	cout<<"Ariba module"<<endl;

	// register us as a sideport listener
	ariba->registerSideportListener(this);

	// generate spovnet name
	Name spovnetName("DynamicMultiplayerGame");
	Name nodeName = Name::UNSPECIFIED;

	// get settings from configuration object
	if (Configuration::haveConfig()){
		Configuration& config = Configuration::instance();

		// get node name
		if (config.exists("node.name")){
			nodeName = config.read<string> ("node.name");
			GameEngine::get().getPlayer().setName(nodeName.toString());
		}

		if (config.exists("node.isInitiator")) {
			AribaNode::isInitiator = true;
			AribaNode::isHost = true;
		}

		// configure ariba module
		if (config.exists("ariba.endpoints"))
			ariba->setProperty("endpoints", config.read<string>("ariba.endpoints"));
		if (config.exists("ariba.bootstrap.hints"))
			ariba->setProperty("bootstrap.hints", config.read<string>("ariba.bootstrap.hints"));
	}

	// start ariba module
	cout<<"start ariba"<<endl;
	ariba->start();

	// create node and join
	node = new Node(*ariba, nodeName);

	// bind NodeListener
	node->bind(this);
	// bind CommunicationListener
	node->bind(this, AribaNode::ARIBANODE_SERVICEID);

	// start node module
	node->start();
	cout<<"Node "<<nodeName.toString()<<" started"<<endl;

	// when initiating, you can define the overlay type, default is Chord [CHORD_OVERLAY]
	SpoVNetProperties params;
	//params.setBaseOverlayType( SpoVNetProperties::ONE_HOP_OVERLAY ); // alternative: OneHop

	// initiate the spovnet
	node->initiate(spovnetName, params);

	// join the spovnet
	node->join(spovnetName);
	cout<<"Spovnet joined"<<endl;

	// ChatParticipant started up...
	myNodeId = node->getNodeId();
}

void AribaNode::shutdown() {
	upDownTimer->stop();
	delete upDownTimer;

	rotationTimer->stop();
	delete rotationTimer;

	groupPingTimer->stop();
	delete groupPingTimer;

	delete messageEvaluator;

	mcpo->shutdown();

	// leave spovnet
	node->leave();

	// unbind node listener
	node->unbind( this );
	// unbind CommunicationListener
	node->unbind( this, AribaNode::ARIBANODE_SERVICEID);

	// deleting mcpo instance
	if (mcpo != NULL) delete mcpo;

	// unregister us from the sideport listener
	ariba->registerSideportListener(NULL);

	// stop the ariba module
	ariba->stop();

	// delete node and ariba module
	delete node;
	delete ariba;
}

void AribaNode::onJoinCompleted( const SpoVNetID& vid ) {
	// initialize the MCPO service
	mcpo = new MCPO(this, ariba, node);

	cout<<"join complete"<<endl;
}

void AribaNode::onJoinFailed(const SpoVNetID& vid) {
}

void AribaNode::onLeaveCompleted(const SpoVNetID& vid){
}

void AribaNode::onLeaveFailed(const SpoVNetID& vid){
}

void AribaNode::receiveData(const DataMessage& msg) {
	GameMessage* gamemsg = msg.getMessage()->convert<GameMessage>();

	cout<<"MCPO Nachricht empfangen. Type: "<<gamemsg->getType()<<" with GroupID: "<<gamemsg->getGroupID()<<"\n";

	if(gamemsg->getGroupID() != 0){
		if(!IsGroupJoined(gamemsg->getGroupID())){
			cout<<"Group not joined"<<"\n";
			return;
		}
	}

	if (gamemsg->getType() != GameMessage::GAME_HOST_OFFER && GameEngine::get().getIsBackup() && GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()) == gamemsg->getGroupID()) {
		boost::posix_time::ptime time = boost::posix_time::microsec_clock::local_time();
		boost::posix_time::time_duration duration( time.time_of_day() );
		lastHostMessageTime = duration.total_milliseconds();
	}

	switch(gamemsg->getType()) {
		case GameMessage::GAME_CONNECT: {
			GameConnect* gcms =
					const_cast<GameMessage*>(gamemsg)->decapsulate<GameConnect>();

			cout<<"GAME_CONNECT MESSAGE FROM "<<gcms->getName()<<"\n";

			//host, no backup, host of the correct region
			if(isHost && !GameEngine::get().getIsBackup() && GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()) == gamemsg->getGroupID())
			{
				//TODO check playername, if sth's going wrong send errorcode (duplicated name, ...)

				//calc new player position
				float posX, posY;
				GameEngine::get().calcSpwanPosition(&posX, &posY);

				//send GAME_CONNECT_ACCEPT to user with new position
				GameMessage gmsg(GameMessage::GAME_CONNECT_ACCEPT);
				ConnectAccept gcamsg(gcms->getName(), posX, posY, time(0));
				gmsg.encapsulate(&gcamsg);
				node->sendMessage(gmsg, node->generateNodeId(gcms->getName()), ARIBANODE_SERVICEID);
				cout<<"SEND GAME_CONNECT_ACCEPT TO "<<gcms->getName()<<" X: "<<posX<<" Y: "<<posY<<"\n";

				//send REGION_CONNECT_ACCEPT to group
				GameMessage gmsg1(GameMessage::REGION_CONNECT_ACCEPT);
				ConnectAccept camsg(gcms->getName(), posX, posY, time(0));
				gmsg1.encapsulate(&camsg);
				sendMessageToGroup(&gmsg1, GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()));
				cout<<"SEND REGION_CONNECT_ACCEPT TO GROUP "<<GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY())<<" Player: "<<gcms->getName()<<" PosX: "<<posX<<" PosY: "<<posY<<"\n";

				//host add new player
				GameEngine::get().addNewPlayer(gcms->getName(), posX, posY);
				//send own status (name, position etc.) direct to new Client
				GameMessage gmsg2(GameMessage::GAME_PLAYERSTATUS);
				PlayerStatus psmsg(GameEngine::get().getPlayer().getName(), GameEngine::get().getPlayer().getX(), GameEngine::get().getPlayer().getY(), time(0));
				gmsg2.encapsulate(&psmsg);
				node->sendMessage(gmsg2, node->generateNodeId(gcms->getName()), ARIBANODE_SERVICEID);

				//send map
				GameMessage gmsg3(GameMessage::GAME_REGION_DATA);
				Region* region = GameEngine::get().getRegion(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY());
				RegionMapMessage rmmsg(region->getMap(), region->getX(), region->getY());
				gmsg3.encapsulate(&rmmsg);
				node->sendMessage(gmsg3, node->generateNodeId(gcms->getName()), ARIBANODE_SERVICEID);
			}

			break;
		}
		case GameMessage::REGION_CONNECT: {
			RegionConnect* rcms =
					const_cast<GameMessage*>(gamemsg)->decapsulate<RegionConnect>();

			cout<<"REGION_CONNECT MESSAGE PLAYER"<<rcms->getName()<<"\n";

			//host, no backup, host of the correct region
			if(isHost && !GameEngine::get().getIsBackup() && GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()) == gamemsg->getGroupID())
			{
				//send REGION_CONNECT_ACCEPT to group
				GameMessage gmsg(GameMessage::REGION_CONNECT_ACCEPT);
				ConnectAccept camsg(rcms->getName(), rcms->getPositionX(), rcms->getPositionY(), time(0));
				gmsg.encapsulate(&camsg);
				sendMessageToGroup(&gmsg, GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()));
				cout<<"SEND REGION_CONNECT_ACCEPT TO GROUP "<<GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY())<<" Player: "<<rcms->getName()<<" PosX: "<<rcms->getPositionX()<<" PosY: "<<rcms->getPositionY()<<"\n";

				//host add new player
				GameEngine::get().addNewPlayer(rcms->getName(), rcms->getPositionX(), rcms->getPositionY());
				//send own status (name, position etc.) direct to new Client
				GameMessage gmsg1(GameMessage::GAME_PLAYERSTATUS);
				PlayerStatus psmsg(GameEngine::get().getPlayer().getName(), GameEngine::get().getPlayer().getX(), GameEngine::get().getPlayer().getY(), time(0));
				gmsg1.encapsulate(&psmsg);
				node->sendMessage(gmsg1, node->generateNodeId(rcms->getName()), ARIBANODE_SERVICEID);

				//send map
				GameMessage gmsg2(GameMessage::GAME_REGION_DATA);
				Region* region = GameEngine::get().getRegion(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY());
				RegionMapMessage rmmsg(region->getMap(), region->getX(), region->getY());
				gmsg2.encapsulate(&rmmsg);
				node->sendMessage(gmsg2, node->generateNodeId(rcms->getName()), ARIBANODE_SERVICEID);
			}
			break;
		}
		case GameMessage::REGION_CONNECT_ACCEPT: {

			ConnectAccept* rcamsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<ConnectAccept>();

			cout<<"REGION_CONNECT_ACCEPT MESSAGE PLAYER: "<<rcamsg->getPlayer()<<" PosX: "<<rcamsg->getPositionX()<<" PosY: "<<rcamsg->getPositionY()<<"\n";

			if(!rcamsg->getPlayer().compare(GameEngine::get().getPlayer().getName()) == 0){
				//add new Player to given position
				GameEngine::get().addNewPlayer(rcamsg->getPlayer(), rcamsg->getPositionX(), rcamsg->getPositionY());

				//send own status (name, position, health etc.) direct to new Client
				GameMessage msg(GameMessage::GAME_PLAYERSTATUS);
				PlayerStatus psmsg(GameEngine::get().getPlayer().getName(), GameEngine::get().getPlayer().getX(), GameEngine::get().getPlayer().getY(), time(0));
				msg.encapsulate(&psmsg);
				node->sendMessage(msg, node->generateNodeId(rcamsg->getPlayer()), ARIBANODE_SERVICEID);
			}
			break;
		}
		case GameMessage::GAME_MOVEUPDOWN: {
			cout<<"GAME_MOVEUPDOWN MESSAGE"<<"\n";

			MoveUpDown* mudmsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<MoveUpDown>();

			cout<<"MOVE PLAYER: "<<mudmsg->getPlayer()<<" TRANSLATION "<<mudmsg->getDirection()<<endl;

			Player* player = GameEngine::get().findPlayer(mudmsg->getPlayer());
			if(player != NULL)
			{
				cout<<"Player "<<mudmsg->getPlayer()<<" found!"<<"\n";

				//check position
				GameEngine::get().checkToBeamPlayer(player, mudmsg->getPositionX(), mudmsg->getPositionY());

				if(!mudmsg->getStop()){
					struct Player::MOVE move;
					move.positionX = mudmsg->getPositionX();
					move.positionY = mudmsg->getPositionY();
					move.timestamp_sender = mudmsg->getTimestamp();
					move.timestamp_receiver = time(0);
					//TODO add current network delay
					move.delay = 0;
					struct Player::MOVE_TRANSLATION moveTranslation;
					moveTranslation.move = move;
					if(mudmsg->getDirection() == 1)
						moveTranslation.translation = Player::UP;
					else
						moveTranslation.translation = Player::DOWN;

					player->setMoveTranslation(moveTranslation);
				}else
				{
					//stop translation
					struct Player::MOVE_TRANSLATION moveTranslation;
					moveTranslation.translation = Player::NONE;
					player->setMoveTranslation(moveTranslation);
				}
			}else
			{
				cout<<"Player "<<mudmsg->getPlayer()<<" NOT found!"<<"\n";
			}
			break;
		}
		case GameMessage::GAME_MOVEROTATION: {
			cout<<"GAME_MOVEROTATION MESSAGE"<<"\n";

			MoveRotation* mrmsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<MoveRotation>();

			cout<<"MOVE PLAYER: "<<mrmsg->getPlayer()<<" ROTATION "<<mrmsg->getDirection()<<endl;

			Player* player = GameEngine::get().findPlayer(mrmsg->getPlayer());
			if(player != NULL)
			{
				//check position
				GameEngine::get().checkToBeamPlayer(player, mrmsg->getRotation(), mrmsg->getPositionX(), mrmsg->getPositionY());

				if(!mrmsg->getStop()){
					struct Player::MOVE move;
					move.positionX = mrmsg->getPositionX();
					move.positionY = mrmsg->getPositionY();
					move.timestamp_sender = mrmsg->getTimestamp();
					move.timestamp_receiver = time(0);
					//TODO add current network delay
					move.delay = 0;
					struct Player::MOVE_ROTATION moveRotation;
					moveRotation.move = move;
					if(mrmsg->getDirection() == 0)
						moveRotation.rotation = Player::RIGHT;
					else
						moveRotation.rotation = Player::LEFT;

					player->setMoveRotation(moveRotation);
				}else
				{
					//stop translation
					struct Player::MOVE_ROTATION moveRotation;
					moveRotation.rotation = Player::NONE;
					player->setMoveRotation(moveRotation);
				}
			}
			break;
		}
		case GameMessage::GAME_SHOT: {
			cout<<"GAME_SHOT MESSAGE"<<"\n";

			ShotMessage* mmsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<ShotMessage>();

			// FIXME just for testing purposes
			cout<<"XXXXXX SHOT: VelocityX "<<mmsg->getVelocityX()<<" VelocityY "<<mmsg->getVelocityY()<<endl;
			Shot* shot = new Shot();
			shot->setX(mmsg->getPositionX());
			shot->setY(mmsg->getPositionY());
			shot->setVelocityX(mmsg->getVelocityX());
			shot->setVelocityY(mmsg->getVelocityY());

			GameEngine::get().addObject(shot);
			break;
		}
		case GameMessage::GAME_HOST_OFFER: {
			cout<<"GAME_HOST_OFFER MESSAGE"<<"\n";

			if(GameEngine::get().getIsBackup() && GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()) == gamemsg->getGroupID()) {
				StringContainerMessage* scmsg =
						const_cast<GameMessage*>(gamemsg)->decapsulate<StringContainerMessage>();

				if (scmsg->getData().compare(nextHostName) > 0) {
					nextHostName = scmsg->getData();
				}
			}
			break;
		}
	}
}

void AribaNode::onMessage(const DataMessage& msg, const ariba::NodeID& remote, const ariba::LinkID& lnk) {
	GameMessage* gamemsg = msg.getMessage()->convert<GameMessage>();

	cout<<"Nachricht empfangen. Type: "<<gamemsg->getType()<<"\n";

	if (messageEvaluator != NULL)
		messageEvaluator->evaluate(*gamemsg, remote);

	// TODO move all this code into a MessageEvaluator
	switch(gamemsg->getType()) {
		case GameMessage::GAME_CONNECT_ACCEPT: {

			ConnectAccept* gcamsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<ConnectAccept>();

			cout<<"GAME_CONNECT_ACCEPT MESSAGE \n";

			GameEngine::get().getPlayer().setX(gcamsg->getPositionX());
			GameEngine::get().getPlayer().setY(gcamsg->getPositionY());
			GameEngine::get().addObject(&GameEngine::get().getPlayer());

			break;
		}
		case GameMessage::GAME_PLAYERSTATUS: {
			PlayerStatus* psmsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<PlayerStatus>();

			cout<<"GAME_PLAYERSTATUS FROM "<<psmsg->getPlayer()<<"\n";

			//check if Player is unknown
			if(GameEngine::get().findPlayer(psmsg->getPlayer()) == NULL)
			{
				//add him
				GameEngine::get().addNewPlayer(psmsg->getPlayer(), psmsg->getPositionX(), psmsg->getPositionY());
			}
			break;
		}
		case GameMessage::GAME_REGION_ALLOCATION: {
			cout<<"GAME_REGION_ALLOCATION MESSAGE"<<"\n";

			RegionAllocationMessage* mmsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<RegionAllocationMessage>();

			cout<<"ASSIGNMENT TO " << mmsg->getX() << ":" << mmsg->getY()<<"; "<<mmsg->getIsBackup()<<endl;

			stringstream s;
			s << "REGION_" << mmsg->getX() << ":" << mmsg->getY();
			node->put(stod(s.str()), stod(node->getName()), 0, true);
			GameEngine::get().manageRegion(mmsg->getX(), mmsg->getY(), mmsg->getIsBackup());
			if (mmsg->getIsBackup()) {
				hostTimeoutTimer->start();
			}
			else {
				isHost = true;
				groupPingTimer->start();
			}

			break;
		}
		case GameMessage::GAME_REGION_DATA: {
			cout<<"GAME_REGION_DATA MESSAGE"<<"\n";

			RegionMapMessage* rmmsg =
					const_cast<GameMessage*>(gamemsg)->decapsulate<RegionMapMessage>();

			GameEngine::get().replaceRegionMap(rmmsg->getMap(), rmmsg->getRegionX(), rmmsg->getRegionY());
			break;
		}
	}
}

void AribaNode::serviceIsReady() {
	ready = true;
	cout<<"Service is ready"<<"\n";

	cout<<"Checking for initiator"<<"\n";

	node->get(stod("REGION_0:0"), AribaNode::ARIBANODE_SERVICEID);
}

void AribaNode::sendShot(Shot* shot){
	GameMessage msg(GameMessage::GAME_SHOT);
	//Name, UpDown, xCoor, yCoor, StopFlag, Timestamp
	ShotMessage mmsg(node->getName(), time(0));
	cout<<"SEND SHOT: VelocityX "<<shot->getVelocityX()<<" VelocityY "<<shot->getVelocityY()<<"\n";

	mmsg.setVelocityX(shot->getVelocityX());
	mmsg.setVelocityY(shot->getVelocityY());
	mmsg.setPositionX(shot->getX());
	mmsg.setPositionY(shot->getY());
	msg.encapsulate(&mmsg);
	//sendMessageToGroup(&msg, GameEngine::get().calcGroupID(GameEngine::get().getCurrentRegionX(), GameEngine::get().getCurrentRegionY()));
	sendMessageToRegions(&msg);
}

//bool flag (1 == up, 0 == down)
void AribaNode::sendUpDownKeyMessage(bool upDown){
	if(!upDownTimer->isFlagSet(upDown) && !upDownTimer->isFlagSet(!upDown))
	{
		cout<<"Start pressing "<<upDown<<endl;
		//start timer
		upDownTimer = new MessageUpDownTimer(this);
		upDownTimer->setInterval(TimerMoveIntervall, false);
		upDownTimer->start();
		upDownTimer->setFlag(upDown);
		//send first message
		MessageUpDownTimerEvent();
	}
}

void AribaNode::sendRotationMessage(bool rightLeft){
	if(!rotationTimer->isFlagSet(rightLeft) && !rotationTimer->isFlagSet(!rightLeft))
	{
		cout<<"Start pressing "<<rightLeft<<endl;
		//start timer
		rotationTimer = new MessageRotationTimer(this);
		rotationTimer->setInterval(TimerMoveIntervall, false);
		rotationTimer->start();
		rotationTimer->setFlag(rightLeft);
		//send first message
		MessageRotationTimerEvent();
	}
}

void AribaNode::stopUpDownKeyMessage(bool upDown){
	if(upDownTimer->isFlagSet(upDown)){
		cout<<"Stop pressing "<<upDown<<endl;

		upDownTimer->clearFlag(upDown);
		upDownTimer->stop();

		GameMessage msg(GameMessage::GAME_MOVEUPDOWN);
		//Name, UpDown, xCoor, yCoor, StopFlag, Timestamp
		MoveUpDown mudmsg(GameEngine::get().getPlayer().getName(), upDown, 0, 0 ,1 ,time(0));

		mudmsg.setPositionX(GameEngine::get().getPlayer().getX());
		mudmsg.setPositionY(GameEngine::get().getPlayer().getY());

		msg.encapsulate(&mudmsg);
		sendMessageToGroup(&msg, GameEngine::get().calcGroupID(GameEngine::get().getCurrentRegionX(), GameEngine::get().getCurrentRegionY()));
	}
}

void AribaNode::stopRotationMessage(bool rightLeft){
	if(rotationTimer->isFlagSet(rightLeft)){
		cout<<"Stop pressing "<<rightLeft<<endl;

		rotationTimer->clearFlag(rightLeft);
		rotationTimer->stop();

		GameMessage msg(GameMessage::GAME_MOVEROTATION);
		//Name, UpDown, xCoor, yCoor, StopFlag, Timestamp
		MoveRotation mrmsg(GameEngine::get().getPlayer().getName(), rightLeft, GameEngine::get().getPlayer().getRotation(), GameEngine::get().getPlayer().getX(), GameEngine::get().getPlayer().getY(), 1, time(0));
		msg.encapsulate(&mrmsg);
		sendMessageToGroup(&msg, GameEngine::get().calcGroupID(GameEngine::get().getCurrentRegionX(), GameEngine::get().getCurrentRegionY()));
	}
}

void AribaNode::MessageUpDownTimerEvent(){
	cout<<"Send MoveUpDownMessage with direction "<<upDownTimer->getFlag()<<endl;

	GameMessage msg(GameMessage::GAME_MOVEUPDOWN);
	//Name, UpDown, xCoor, yCoor, StopFlag, Timestamp
	MoveUpDown mudmsg(GameEngine::get().getPlayer().getName(), upDownTimer->getFlag(), 0, 0 ,0 ,time(0));

	// FIXME just for testing purposes
	mudmsg.setPositionX(GameEngine::get().getPlayer().getX());
	mudmsg.setPositionY(GameEngine::get().getPlayer().getY());

	msg.encapsulate(&mudmsg);
	sendMessageToGroup(&msg, GameEngine::get().calcGroupID(GameEngine::get().getCurrentRegionX(), GameEngine::get().getCurrentRegionY()));
}

void AribaNode::MessageRotationTimerEvent(){
	cout<<"Send MoveRotationMessage with direction "<<rotationTimer->getFlag()<<endl;

	GameMessage msg(GameMessage::GAME_MOVEROTATION);
	//Name, UpDown, xCoor, yCoor, StopFlag, Timestamp
	MoveRotation mrmsg(GameEngine::get().getPlayer().getName(), rotationTimer->getFlag(), GameEngine::get().getPlayer().getRotation(), GameEngine::get().getPlayer().getX(), GameEngine::get().getPlayer().getY(), 0, time(0));
	msg.encapsulate(&mrmsg);
	sendMessageToGroup(&msg, GameEngine::get().calcGroupID(GameEngine::get().getCurrentRegionX(), GameEngine::get().getCurrentRegionY()));
}

void AribaNode::JoinGroup(int groupID){
	if(!IsGroupJoined(groupID)) {
		groups.push_back(groupID);
		//mcpo->joinGroup(groupID);
	}
}

void AribaNode::LeaveGroup(int groupID){
	groups.remove(groupID);
	//mcpo->leaveGroup(groupID);
}

bool AribaNode::IsGroupJoined(int groupID){
	if(groupID == 0)
		return true;

	return find(groups.begin(), groups.end(), groupID) != groups.end();
}

void AribaNode::onKeyValue( const Data& key, const vector<Data>& value ) {
	if (value.size() == 0) {
		cout<<"Received DHT answer for '" << dtos(key)
				<< "': no values stored! "<<endl;

		if (dtos(key) == "REGION_0:0") {
			// TODO publish start event
			node->put(stod("REGION_0:0"), stod(node->getName()), 0, true);
			AribaNode::isInitiator = true;
			AribaNode::isHost = true;
			messageEvaluator = new InitiatorMessageEvaluator();

			//TODO: calc region with spawnpoint and join them
			float posX, posY;
			GameEngine::get().calcSpwanPosition(&posX, &posY);
			GameEngine::get().getPlayer().setX(posX);
			GameEngine::get().getPlayer().setY(posY);
			GameEngine::get().addObject(&GameEngine::get().getPlayer());
		}

		return;
	}
	cout<<"Received DHT answer for '" << dtos(key) << "' "
			<< " with value='" << dtos(value.front()) << "'."<<endl;

	if (dtos(key) == "REGION_0:0") {
		// initiator exists, ping him
		ariba::NodeID initiatorNodeID = node->generateNodeId(dtos(value.front()));

		GameMessage msg(GameMessage::GAME_PING);
		node->sendMessage(msg, initiatorNodeID, ARIBANODE_SERVICEID);

		//FIXME !TEST! connect to Region 0/0
		connectToGame(GameEngine::get().calcGroupID(0,0));
	}

	if (dtos(key) == "MAP_WIDTH") {
		GameEngine::get().setMapWidth(stoi(dtos(value.front())));
	}

	if (dtos(key) == "MAP_HEIGHT") {
		GameEngine::get().setMapHeight(stoi(dtos(value.front())));
	}
}

void AribaNode::publish() {
	messageEvaluator->onGetReady();
}

Node& AribaNode::getNode() {
	return *node;
}

void AribaNode::connectToGame(int groupID){
	JoinGroup(groupID);
	GameMessage msg(GameMessage::GAME_CONNECT);
	GameConnect gcmsg(node->getName(), time(0));
	msg.encapsulate(&gcmsg);
	cout<<"SEND GAME_CONNECT MESSAGE TO GROUP: "<<groupID<<"\n";

	sendMessageToGroup(&msg, groupID);
}

/**
 * Connect to Region
 */
void AribaNode::connectToRegion(int groupID) {
	JoinGroup(groupID);
	GameMessage msg(GameMessage::REGION_CONNECT);
	RegionConnect rcmsg(node->getName(), GameEngine::get().getPlayer().getX(), GameEngine::get().getPlayer().getY(), time(0));
	msg.encapsulate(&rcmsg);
	cout<<"SEND REGION_CONNECT MESSAGE TO GROUP: "<<groupID<<"PosX: "<<GameEngine::get().getPlayer().getX()<<" PosY: "<<GameEngine::get().getPlayer().getY()<<"\n";

	sendMessageToGroup(&msg, groupID);
}

void AribaNode::sendMessageToGroup(GameMessage* msg, int groupID) {
	if (GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()) == groupID)
		groupPingTimer->reset();
	msg->setGroupID(groupID);
	mcpo->sendToAll(msg);
}

void AribaNode::sendMessageToNode(GameMessage msg, ariba::utility::NodeID nodeID) {
	node->sendMessage(msg, nodeID, ARIBANODE_SERVICEID);
}

void AribaNode::sendMessageToRegions(GameMessage* msg){
	list<Region*> regions = GameEngine::get().getJoinedRegions();
	for (list<Region*>::iterator it = regions.begin(); it != regions.end(); it++) {
		Region* region = *it;
		sendMessageToGroup(msg, GameEngine::get().calcGroupID(region->getX(), region->getY()));
	}
}

void AribaNode::GroupPingTimerEvent() {
	cout<<"SENDING PING"<<endl;
	GameMessage msg(GameMessage::GAME_PING);
	sendMessageToGroup(&msg, GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()));
}

void AribaNode::HostTimeoutTimerEvent() {
	boost::posix_time::ptime time = boost::posix_time::microsec_clock::local_time();
	boost::posix_time::time_duration duration( time.time_of_day() );
	int hostOfflineTime = duration.total_milliseconds() - lastHostMessageTime;
	if (hostOfflineTime >= 2 * HostTimeoutTimerIntervall) {
		if (node->getName() == nextHostName) { // new host
			GameEngine::get().manageRegion(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY(), false);
			isHost = true;
			groupPingTimer->start();
			hostTimeoutTimer->stop();
		}
		cout<<"ELECTED "<<nextHostName<<endl;
		lastHostMessageTime = duration.total_milliseconds();
	}
	else if (hostOfflineTime >= HostTimeoutTimerIntervall) {
		cout<<"STARTING ELECTION"<<endl;
		nextHostName = node->getName();
		GameMessage gmsg(GameMessage::GAME_HOST_OFFER);
		StringContainerMessage scmsg(node->getName());
		gmsg.encapsulate(&scmsg);
		sendMessageToGroup(&gmsg, GameEngine::get().calcGroupID(GameEngine::get().getManagedRegionX(), GameEngine::get().getManagedRegionY()));
	}
}

bool AribaNode::isReady() {
	return ready;
}
